/*
    :copyright: 2011 by Florian Boesch <pyalot@gmail.com>.
    :license: GNU AGPL3, see LICENSE for more details.
*/
vertex:
    attribute vec3 position;
    attribute vec2 texcoord;
    varying vec2 uv;

    void main(void) {
        gl_Position = vec4(position, 1.0);
        uv = texcoord;
    }

fragment:
    #define sample_count 16
    #define pattern_size 4.0

    uniform sampler2D normaldepth, random_field;
    uniform vec2 viewport;
    uniform float near, far, radius, epsilon, full_occlusion_treshold, no_occlusion_treshold, occlusion_power, random_size, power;
    uniform mat4 proj, inv_proj;
    uniform mat3 inv_rot;
    uniform vec3 samples[8];
    varying vec2 uv;
    
    vec3 getsample(int i){
        vec2 mod_coord = mod(floor(gl_FragCoord.xy), pattern_size);
        float y = ((mod_coord.x + mod_coord.y*pattern_size)+0.5) / (pattern_size*pattern_size);
        float x = (float(i)+0.5)/float(sample_count);
        return (texture2D(random_field, vec2(x, y)).xyz-0.5)*2.0;
    }

    vec3 get_eye_normal(){
        vec2 frag_coord = gl_FragCoord.xy/viewport;
        frag_coord = (frag_coord-0.5)*2.0;
        vec4 device_normal = vec4(frag_coord, 0.0, 1.0);
        return normalize((inv_proj * device_normal).xyz);
    }

    vec3 decode_normal(vec2 enc){
        vec2 fenc = enc*4.0-2.0;
        float f = dot(fenc,fenc);
        float g = sqrt(1.0-f/4.0);
        return vec3(fenc*g, 1.0-f/2.0);
    }

    float decode_depth(vec2 src){
        float depth = src.x/255.0+src.y;
        return depth*far+near;
    }

    float occlusionFunction(float dist){
        if(dist > epsilon){
            if(dist < full_occlusion_treshold){
                return 1.0;
            }
            else{
                float range = no_occlusion_treshold - full_occlusion_treshold;
                return max(1.0 - pow((dist - full_occlusion_treshold)/range, occlusion_power), 0.0);
            }
        }
        else{
            return 0.0;
        }
    }

    float testOcclusion(vec3 eye_normal, vec3 eye_pos, vec3 sample_offset){
        sample_offset *= inv_rot;
        sample_offset *= sign(dot(eye_normal, sample_offset));
        vec3 sample_pos = eye_pos + radius*sample_offset;
        vec4 device = proj * vec4(sample_pos, 1.0);
        vec4 device_norm = device/device.w;
        vec2 screen_coord = (device_norm.xy+1.0)*0.5;

        vec4 sample_data = texture2D(normaldepth, screen_coord);
        float sample_depth = decode_depth(sample_data.zw);
        float dist = length(sample_pos) - sample_depth;
        return occlusionFunction(dist)*dot(normalize(sample_offset), eye_normal);
        //return occlusionFunction(dist);
    }

    void main(void){
        vec3 eye_ray = get_eye_normal();
        vec4 eye_data = texture2D(normaldepth, uv);
        vec3 eye_normal = decode_normal(eye_data.xy);
        float eye_depth = decode_depth(eye_data.zw);
        vec3 eye_pos = eye_depth * eye_ray;
        float result = 0.0;
        
        for(int i=0; i<sample_count; i++){
            //vec3 sample_offset = samples[i];
            vec3 sample_offset = getsample(i);
            result += testOcclusion(eye_normal, eye_pos, sample_offset);
        }
        result /= float(sample_count);
        gl_FragColor = vec4(pow(1.0-result, power));
    }